/*********************************************************************************
FileName: TheBuffer.c

Description: Implementation des fonctions de service du pilote-caractere
			 "Char Driver" TheBuffer.
			 Le module TheBuffer contient les fonctions suivantes:
			 - buf_init
			 - buf_exit
			 - buf_open
			 - buf_release
			 - buf_read
			 - buf_write
			 - buf_ioctl

Authors: Vincent Leger et Francisco Jesus Ramos Reyes

Last Modification: 28-10-2012

Modifications: --
*********************************************************************************/

#include "TheBuffer.h"
#include "RingBuffCore.c"


/*********************************************************************************
Function name : static int __init buf_init(void)
Brief: Initialise le module TheBuffer et alloue de la memoire
	   pour les tempons utilises.
-Initialisation des structures personnelles;
-Allocation d’un MAJOR/MINOR;
-Création du char driver (cdev);
-Création de la classe et du device.

Inputs: pwas de Inputs (void)
Outputs: pas de Outputs
Return value: static int --> utilise pour le retour de code d'erreur

Last modified: 28-10-2012
notes: exemple d'utilisation et call out -- module_init(buf_init);
*********************************************************************************/

static int __init buf_init(void)
{
    int result;
    dev_t devno;
    struct device *Buffer_device;

    result = 0;
    Buffer_minor = 0;
    nbDevices = 1;

    /************ALLOCATION D'UN NUMERO D'UNITE-MATERIEL*************************/
    result = alloc_chrdev_region(&devno, Buffer_minor, nbDevices, DRIVER_NAME);
    if (result < 0)
    {
        DEBUG_VALUE("ERROR ERROR The BUFFER: can't get major ", Buffer_major);
        return result;
    }
    Buffer_major = MAJOR(devno);

    /**creation d'une classe pour le kernel avec nom de driver: TheBuffer. et le
 	 owner est THIS_MODULE (self)***********************************************/
    BufferClass = class_create(THIS_MODULE, DRIVER_NAME);
    if (IS_ERR(BufferClass))
    {
        DEBUG_MESSAGE("ERROR ERROR The BUFFER: can't create class");
        //chrdev region must be unallocated before exit
        unregister_chrdev_region(devno, 1);
        DEBUG_MESSAGE("Ring Buffer uninstalled");
        return -EBUSY;
    }

    /* Initialization des valeurs de la structure Bufstruct du tempon circulaire**/
    Buffer.BufSize = DEFAULT_BUFSIZE;
    Buffer.InIdx= 0;
    Buffer.OutIdx = 0;
    Buffer.BufFull = 0;
    Buffer.BufEmpty = 1;

    /*allocate memory for buffers (ring buffer, local read buffer and local write buffer)*/
    Buffer.Buffer = (unsigned char*) kmalloc(DEFAULT_BUFSIZE * sizeof(unsigned char),0);
    BDev.ReadBuf = (unsigned char *) kmalloc(READWRITE_BUFSIZE * sizeof(unsigned char),0);
    BDev.WriteBuf = (unsigned char *) kmalloc(READWRITE_BUFSIZE * sizeof(unsigned char),0);
    //trap errors
    if (IS_ERR(Buffer.Buffer) || IS_ERR(BDev.ReadBuf) || IS_ERR(BDev.WriteBuf))
	{
	    DEBUG_MESSAGE("ERROR ERROR The BUFFER: can't allocate memory for Buffers !!!");
	    kfree(Buffer.Buffer);
	    kfree(BDev.ReadBuf);
	    kfree(BDev.WriteBuf);
	    //release everything that's been made so far...
		//destroy class
    	class_destroy(BufferClass);
    	//chrdev...
    	unregister_chrdev_region(devno, 1);
	    return -EBUSY;
	}

    //write entry to cdev
    cdev_init(&BDev.cdev, &Buf_fops);
    BDev.cdev.owner = THIS_MODULE;  //owner
    BDev.cdev.ops = &Buf_fops;      //file functions

    /* initialisation de semaphores */
    sema_init(&BDev.SemBuf, 1);
    //sema_init(&BDev.SemWrite, 1); //not used... only one writer MAX
    sema_init(&BDev.SemRead, 1);

    //add device
    result = cdev_add(&BDev.cdev, devno, 1);
    if (result)
    {
        //then again, everything must be destroyed..
        DEBUG_VALUE("ERROR ERROR adding The BUFFER\nError = ", (int)result);
    	//free Buffers memory (ring buffer, local read buffer and local write buffer)
    	kfree(Buffer.Buffer);
    	kfree(BDev.ReadBuf);
    	kfree(BDev.WriteBuf);
    	//pointers to NULL
    	BDev.ReadBuf = NULL;
    	BDev.WriteBuf = NULL;
    	Buffer.Buffer = NULL;

    	//DESTROY ALL OBJECTS
    	//get devno
		//VINCE: NOT NEEDED --> devno already have good value from initialization beginning
    	//devno = MKDEV(Buffer_major, Buffer_minor);
    	//destroy class
    	class_destroy(BufferClass);
    	//chrdev...
    	unregister_chrdev_region(devno, 1);
        DEBUG_MESSAGE("Ring Buffer uninstalled");

        return -EBUSY;
    }

    //create device
    Buffer_device = device_create(BufferClass, NULL, devno, &BDev, DRIVER_NAME);
    if (IS_ERR(Buffer_device))
    {
        //EXTERMINATE EVERYTHING
        DEBUG_MESSAGE("ERROR ERROR TheBuffer: can't create device");
    	//free Buffers memory (ring buffer, local read buffer and local write buffer)
    	kfree(Buffer.Buffer);
    	kfree(BDev.ReadBuf);
    	kfree(BDev.WriteBuf);
    	//pointers to NULL
    	BDev.ReadBuf = NULL;
    	BDev.WriteBuf = NULL;
    	Buffer.Buffer = NULL;

    	//DESTROY ALL OBJECTS
    	//get devno
    	devno = MKDEV(Buffer_major, Buffer_minor);
        //destroy device
        device_destroy(BufferClass, devno);
        //delete cdev
        cdev_del(&BDev.cdev);
        //destroy class
        class_destroy(BufferClass);
        //chrdev...
        unregister_chrdev_region(devno, 1);
        DEBUG_MESSAGE("Ring Buffer uninstalled");

        return -EBUSY;
    }


    DEBUG_VALUE("The BUFFER- is initialized \nMAJOR = ", Buffer_major);
    DEBUG_VALUE("Minor = ", Buffer_minor);
    return 0;
}

/*********************************************************************************
Function name : static void __exit buf_exit(void)
Brief: Libere la memoire et detruit le module TheBuffer.
-deallocation du MAJOR/MINOR;
-detruit le char driver (cdev);
-detruit la classe et le device.

Inputs: pas de Inputs (void)
Outputs: pas de Outputs
Return value: static int --> utilise pour le retour de code d'erreur

Last modified: 28-10-2012//chrdev...
    	unregister_chrdev_region(devno, 1);
        DEBUG_MESSAGE("Ring Buffer uninstalled");
notes: exemple d'utilisation et call out -- module_exit(buf_exit);
*********************************************************************************/
static void __exit buf_exit(void)
{

	dev_t devno;

	//free Buffers memory (ring buffer, local read buffer and local write buffer)
	kfree(Buffer.Buffer);
	kfree(BDev.ReadBuf);
	kfree(BDev.WriteBuf);
	//pointers to NULL
	BDev.ReadBuf = NULL;
	BDev.WriteBuf = NULL;
	Buffer.Buffer = NULL;

	//DESTROY ALL OBJECTS
	//get devno
	devno = MKDEV(Buffer_major, Buffer_minor);
    //destroy device
    device_destroy(BufferClass, devno);
    //delete cdev
    cdev_del(&BDev.cdev);
    //destroy class
    class_destroy(BufferClass);
    //chrdev...
    unregister_chrdev_region(devno, 1);

    DEBUG_MESSAGE("Ring Buffer uninstalled");

}

/*********************************************************************************
Function name : int buf_open(struct inode *inode, struct file *filp)

Brief: -Utilise par l'usage pour ouvrire le pilote et pouvoir utiliser ses
		fonctionalites a traver la structure file_operations.
	   -Dans cette fonction l'usage indique le mode d'ouverture et determine
	    ainsi les fonctions auquel il a le droit d'acceder.
	   -Initialiser l'uinite-materiel lorsqu'elle est ouverte pour la
	    premiere fois.

Inputs: struct inode -->Pointeurs de fonctions pour le noyau
		struct file  -->Pointeurs de fonctions pour les usages
Outputs: Int
Return value: int --> utilise pour le retour de code d'erreur

Last modified: 28-10-2012
notes: exemple d'utilisation et call out -- fd = open("/dev/TheBuffer", O_RDWR);
*********************************************************************************/
int buf_open(struct inode *inode, struct file *filp)
{
    int returnValue;

    returnValue = 0;
 
	
    //todo: set private data structure
    //(not really useful though since using same buffers for all users...
    //could just use global struct)

    //now check for open flags (read / write)
    //check BLOCK flag
    if (filp->f_flags & O_NONBLOCK)
    {
        DEBUG_MESSAGE("NON_BLOCK");
	    //check sem (non-block style)
        if(down_trylock(&BDev.SemBuf))
        {
            return -EAGAIN;   //return error immediately
        }
    }
    else
    {
        DEBUG_MESSAGE("BLOCK");
	    //else wait for Sem
        if (down_interruptible(&BDev.SemBuf))
        {   //if it gets interrupted...
            return -ERESTARTSYS;
        }
    }

    //check file open mode...
    if((filp->f_flags & O_ACCMODE) == O_RDONLY) //read only
    {
        if(BDev.numReader < MAX_READERS)        //note: number of reader is unreachable. A max exist to avoid variable overflow.
        {   //count every single reader that connects
            BDev.numReader++;
            filp->private_data = &BDev;
        }
        else
        {
            returnValue = -ENOTTY;   //can't have any more readers...
        }
    }
    else if((filp->f_flags & O_ACCMODE) == O_WRONLY) //write only
    {
        //check number writers... then suscript as writer
        if(BDev.numWriter < MAX_WRITERS)             //note: only 1 writer MAX
        {
            BDev.numWriter++; //one max... could be used as flag (numWriter = 1), but it would be easier to increase max number writers that way
            filp->private_data = &BDev;  //associate file's private data to BDev structure
        }
        else
        {
            returnValue = -ENOTTY;   //can't have any more writers...
        }
    }
    else if((filp->f_flags & O_ACCMODE) == O_RDWR) //read + write
    {
        //do both...
        if((BDev.numWriter < MAX_WRITERS)&&(BDev.numReader < MAX_READERS))
        {
            BDev.numReader++;
            BDev.numWriter++;
            filp->private_data = &BDev;
        }
        else
        {
            returnValue = -ENOTTY;   //can't have any more writers...
        }
    }
    else
    {
    	return -EBUSY;
    }
    //release SEM
    up(&BDev.SemBuf);

    //printk(KERN_WARNING "OPENING The BUFFER\n");
    DEBUG_MESSAGE("OPENING The BUFFER");
    return returnValue;
}

/*********************************************************************************
Function name : int buf_release(struct inode *inode, struct file *filp)

Brief: -Utilise par l'usage pour liberer le pilote quan il n'a plus besoin
		de ses services.
	   -Arrete le materiel a la derniere liberation.

Inputs: struct inode -->Pointeurs de fonctions pour le noyau
		struct file  -->Pointeurs de fonctions pour les usages
Outputs: Int
Return value: int --> utilise pour le retour de code d'erreur

Last modified: 28-10-2012
notes: exemple d'utilisation et call out -- close(fd);
*********************************************************************************/

int buf_release(struct inode *inode, struct file *filp)
{
	int returnValue;
	struct Buf_Dev *BuffDev;

	BuffDev = filp->private_data;

	returnValue = 0;



	//check file open mode...
	//wait for sem
	if (filp->f_flags & O_NONBLOCK)
    {
        if(down_trylock(&BuffDev->SemBuf))
        {
            return -EAGAIN;
        }
    }
    else
    {
        if (down_interruptible(&BuffDev->SemBuf))
        {
            return -ERESTARTSYS;
        }
    }

    if((filp->f_flags & O_ACCMODE) == O_RDONLY) //read only
    {
        if (BuffDev->numReader > 0) //robustness check
        {
            BuffDev->numReader--;
        }
    }
    else if((filp->f_flags & O_ACCMODE) == O_WRONLY) //write only
    {
        //release writer rights
        if (BuffDev->numWriter > 0) //robustness check
        {
            BuffDev->numWriter--;
        }
    }
    else if((filp->f_flags & O_ACCMODE) == O_RDWR) //read + write
    {
        //do both...
        if (BuffDev->numWriter > 0) //robustness check
        {
            BuffDev->numWriter--;
        }
        if (BuffDev->numReader > 0) //robustness check
        {
            BuffDev->numReader--;
        }
    }
    else
    {
    	returnValue = -EBUSY;
    }
	//release SEM
    up(&BuffDev->SemBuf);

    filp->private_data = NULL;
    DEBUG_MESSAGE("Releasing The BUFFER");
    return returnValue;
}

/*********************************************************************************
Function name : ssize_t buf_read(struct file *filp, char __user *ubuf,
                    size_t count, loff_t *f_ops)
Brief: -Quand l'usage veut des donnees du materiel, c'est cette fonction
		qui est appele.

Inputs: struct file  	  -->Pointeurs de fonctions pour les usages
		char __user *ubuf -->Pointeur de la destination ou le data sera copie
		size_t count	  -->Qte de data qui sera copie
		loff_t *f_ops	  -->
Outputs: ssize_t --> Qte de data copie avec succes
Return value: ssize_t --> Qte de data copie avec succes

Last modified: 28-10-2012
notes: exemple d'utilisation et call out -- read(fd, TestBufferIn, CHAR_NUM);
*********************************************************************************/
ssize_t buf_read(struct file *filp, char __user *ubuf,
                    size_t count, loff_t *f_ops)
{
	ssize_t qtyRead;
	unsigned int i;
	unsigned int qtyToRead;
	struct Buf_Dev* BuffDev;

	BuffDev = filp->private_data;

    DEBUG_MESSAGE("In buf_read fonction");

	i = 0;
	qtyRead = 0;
	qtyToRead = 0;

	//verify qty of data requested and buffer open mode
	if ((((filp->f_flags)& O_ACCMODE)==O_RDONLY)||(((filp->f_flags)& O_ACCMODE)==O_RDWR))
	{
		if (count < 0)
		{
			count = 0;
		}
	    //get SEM
	    while(count>0)
	    {

	        if(count > READWRITE_BUFSIZE)
	        {   //get maximum transmit qty
	            qtyToRead = READWRITE_BUFSIZE;
	        }
	        else
	        {
	            qtyToRead = count;
	        }
			//compute what's left to be transmitted
	        count = count - qtyToRead;
	        DEBUG_VALUE("Qty to read ",qtyToRead);

			//check block mode flag
	        if (filp->f_flags & O_NONBLOCK)
            {
	            DEBUG_MESSAGE("NON_BLOCK");
                if(down_trylock(&BuffDev->SemRead))
                {
                    return -EAGAIN;
                }
            }
            else
            {
                DEBUG_MESSAGE("BLOCK");
                if (down_interruptible(&BuffDev->SemRead))
                {
                    return -ERESTARTSYS;
                }
            }
	        //clear read buffer
	        for(i=0;i<READWRITE_BUFSIZE;i++)
	            BuffDev->ReadBuf[i] = 0;
	        for(i=0;i<qtyToRead;i++)
            {
	            //SEM
	            if (filp->f_flags & O_NONBLOCK)
                {
                    if(down_trylock(&BuffDev->SemBuf))
                    {
					    //release read semaphore before exiting
                        up(&BuffDev->SemRead);
                        return -EAGAIN;
                    }
                }
                else
                {
                    if (down_interruptible(&BuffDev->SemBuf))
                    {   //same here, release read sem
                        up(&BuffDev->SemRead);
                        return -ERESTARTSYS;
                    }
                }
                if (BufOut(&Buffer,BuffDev->ReadBuf+i)==0)
                {
                    qtyRead++;
                }
                else
                {
                    count = 0;  //force return (buffer is empty);
                    i = qtyToRead;  //force break
                    //break;  //could also be i=qtyToRead to avoir "break"
                }
                up(&BuffDev->SemBuf);

                DEBUG_VALUE("Reading # ",i);
                DEBUG_CHARACTER("Sent to BuffDev->ReadBuf: ", BuffDev->ReadBuf[i]);
            }
	        //release SEM
	        //todo:add read/write buff sems...
	        copy_to_user((char __user*) ubuf, (void*) BuffDev->ReadBuf,i);
	        up(&BuffDev->SemRead);
	    }
	}
	else
	{
	    qtyRead = -EACCES;   //ERROR, access denied (invalid open mode)
	}

    DEBUG_MESSAGE("ubuf Read: ");

    for (i=0;i<CHAR_NUM;i++)
        DEBUG_CHARACTER("", ubuf[i]);

	DEBUG_MESSAGE("Reading from The BUFFER");
	return qtyRead;
}

/*********************************************************************************
Function name : ssize_t buf_write(struct file *filp, const char __user *ubuf,
                    size_t count, loff_t *f_ops)
Brief: -Quand l'usage veut envoyer des donnees au materiel, c'est cette fonction
		qui est appele.

Inputs: struct file  	  -->Pointeurs de fonctions pour les usages
		char __user *ubuf -->Pointeur de la source qui sera copie
		size_t count	  -->Qte de data qui sera copie
		loff_t *f_ops	  -->
Outputs: ssize_t --> Qte de data copie avec succes
Return value: ssize_t --> Qte de data copie avec succes

Last modified: 28-10-2012
notes: exemple d'utilisation et call out -- write(fd, TestBufferOut, CHAR_NUM);
*********************************************************************************/
ssize_t buf_write(struct file *filp, const char __user *ubuf,
                    size_t count, loff_t *f_ops)
{
    ssize_t qtyWrote;
    unsigned int qtyToWrite;
    unsigned int i;

    struct Buf_Dev *BuffDev;

    BuffDev = filp->private_data;

    DEBUG_MESSAGE("In buf_write fonction\nubuf: ");
    for(i=0;i<CHAR_NUM;i++)
        DEBUG_CHARACTER("", ubuf[i]);

    i = 0;
    qtyWrote = 0;
    qtyToWrite = 0;

    if ((((filp->f_flags)& O_ACCMODE)==O_WRONLY)||(((filp->f_flags)& O_ACCMODE)==O_RDWR))
    {
    	if (count < 0)
    	{
    		count = 0;
    	}
        while(count>0)
        {
            DEBUG_MESSAGE("something is happening...");
            DEBUG_VALUE("Count : ", count);

            if(count > READWRITE_BUFSIZE)
            {
                qtyToWrite = READWRITE_BUFSIZE;
            }
            else
            {
                qtyToWrite = count;
            }
            count = count - qtyToWrite;

            //note: no use for SemWrite since only one writer MAX
            //clear write buffer
            for(i=0;i<READWRITE_BUFSIZE;i++)
                BuffDev->WriteBuf[i] = 0;

            copy_from_user((void*)BuffDev->WriteBuf,(const void __user*) ubuf, qtyToWrite);

            DEBUG_VALUE("Quantity to write : ",qtyToWrite);

            for(i=0;i<qtyToWrite;i++)
            {
                DEBUG_VALUE("trying to write ...", i);

                if (filp->f_flags & O_NONBLOCK)
                {
                    if(down_trylock(&BuffDev->SemBuf))
                    {
                        return -EAGAIN;
                    }
                }
                else
                {
                    if (down_interruptible(&BuffDev->SemBuf))
                    {
                        return -ERESTARTSYS;
                    }
                }

                DEBUG_CHARACTER("copy_from_use BuffDev->WriteBuf: ",BuffDev->WriteBuf[i]);

                if (BufIn(&Buffer,BuffDev->WriteBuf+i)==0)
                {
                    DEBUG_CHARACTER("The BUFFER --> BufIn : ",(Buffer.Buffer[i]));
                    qtyWrote++;
                }
                else
                {
                    DEBUG_MESSAGE("no more space...");
                    count = 0;  //force return (buffer is empty);
                    i = qtyToWrite; //force break
                    //break;  //could also be i=qtyToRead to avoir "break"
                }
                up(&BuffDev->SemBuf);
            }
            //release SEM
            //todo:add read/write buff sems...
#           //up(&BuffDev->SemWrite);

        }
    }
    else
    {
        qtyWrote = -EACCES;   //ERROR, access denied (invalid open mode)
    }
    DEBUG_CHARACTER("Writing to The BUFFER\n",BuffDev->WriteBuf[0]);
    DEBUG_CHARACTER("\n",BuffDev->WriteBuf[1]);
    DEBUG_CHARACTER("\n",BuffDev->WriteBuf[2]);
    DEBUG_CHARACTER("\n",BuffDev->WriteBuf[3]);

    DEBUG_CHARACTER("Into The BUFFER\n",Buffer.Buffer[0]);
    DEBUG_CHARACTER("\n",Buffer.Buffer[1]);
    DEBUG_CHARACTER("\n",Buffer.Buffer[2]);
    return qtyWrote;
}

/*********************************************************************************
Function name :long buf_ioctl (struct file *filp,
								unsigned int cmd, unsigned long *arg)

Brief: -Fonction utilise pour envoyer differentes commandes au materiel.

Inputs: struct file  	   -->Pointeurs de fonctions pour les usages
		unsigned long *arg -->Pointeur pour espace d'echange de donnee entre
							  l'usager et le pilote.
		unsigned int cmd   -->Commande envoye par l'usager

Outputs: long --> Qte de data copie avec succes
Return value: long --> utilise pour le retour de code d'erreur

Last modified: 28-10-2012
notes: exemple d'utilisation et call out -- ioctl(fd, THEBUFFER_IOCTL_GET_QTY_DATA
												  , &QtyData);
*********************************************************************************/
long buf_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
    int err;
    int retval;
    int valueFromUser;
    unsigned long valueToUser;
    struct Buf_Dev *BuffDev;

    BuffDev = filp->private_data;

    valueFromUser = 0;
    err = 0;
    retval = 0;


    if (_IOC_TYPE(cmd) != THEBUFFER_IOCTL_MAGIC_NO)
        return -ENOTTY; //check driver
    if (_IOC_NR(cmd) > THEBUFFER_IOCTL_MAXVALUE)
        return -ENOTTY; //check command
    if (_IOC_DIR(cmd) & _IOC_READ)
        err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
    else if (_IOC_DIR(cmd) & _IOC_WRITE)
        err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
    if (err)
        return -EFAULT;

    //check SEM
    if (filp->f_flags & O_NONBLOCK)
    {
        if(down_trylock(&BuffDev->SemBuf))
        {
            return -EAGAIN;
        }
    }
    else
    {
        if (down_interruptible(&BuffDev->SemBuf))
        {
            return -ERESTARTSYS;
        }
    }

    //designer note:
    //user permission is only checked for writing data in ioctl
    //any user that have a valid handle to the file may read ioctl value (even in write only mode)

    //Check if user has sudoer permissions
    if(!capable(CAP_SYS_ADMIN))
    {
	DEBUG_MESSAGE("Pas de privilege de sudo (CAP_SYS_ADMIN)");
	up(&BuffDev->SemBuf);
        return -EPERM;                
    }    
		

    switch(cmd)
    {
        case THEBUFFER_IOCTL_RESET:
            //put everything to default (create defines)
            BufReInit(&Buffer);
            break;
        case THEBUFFER_IOCTL_GET_QTY_DATA:

        	if (Buffer.BufFull)	//buffer is full -> take buffer length
        	{
        		valueToUser = (long)Buffer.BufSize;
        	}
        	else if (Buffer.BufEmpty) //buffer is empty
        	{
        		valueToUser = 0;
        	}
        	else if (Buffer.InIdx > Buffer.OutIdx)	//value is good, InIdx is higher than OutIdx, simply substract
        	{
        		valueToUser = (long)(Buffer.InIdx - Buffer.OutIdx);
        	}
        	else    //value is good, but OutIdx is higher than InIdx -> add buffer size before substracting
        	{
        		valueToUser = (long)((Buffer.InIdx + Buffer.BufSize) - Buffer.OutIdx);
        	}
            retval = __put_user(valueToUser, (unsigned long __user *)arg);   //NOT SURE

            break;
        case THEBUFFER_IOCTL_GET_QTY_READER:
        //	printk(KERN_WARNING "BuffDev->numReader = %u\n", BuffDev->numReader);
                retval = __put_user(BuffDev->numReader, (unsigned long __user *)arg);
            break;
        case THEBUFFER_IOCTL_GET_BUFFERSIZE:
            retval = __put_user(Buffer.BufSize, (unsigned long __user *)arg);
            break;
        case THEBUFFER_IOCTL_SET_BUFFERSIZE:
            //verify value from user (>0 and <MAX_EVER_LENGTH)
        	if((filp->f_flags & O_ACCMODE) == O_RDONLY)	//check if not read only
        	{     
		//(buffer can only be opened as rdonly, wronly or read+write)
        		retval = -EACCES;	//access denied
        	}
        	else
        	{
        		retval = __get_user(valueFromUser, (unsigned long __user *)arg);
				DEBUG_VALUE("new BuffSize : ",valueFromUser);
				BufResize(&Buffer, valueFromUser);
        	}
            break;
        default:
            retval = -ENOTTY;
            break;
    }
    up(&BuffDev->SemBuf);

    return retval;
}


